/*************************************************************************
 * Tic Tac Toe
 *
 * Author: Justin Sheppard
 * Student #: 200811917
 * E-Mail: justin.sheppard@mun.ca
 * ***********************************************************************/
package tictactoe;

/** Game ********************************************************************
 * 
 *  The logic of the game. Used to determine game's status (win, lose, draw),
 *  as well as place player/ai pieces.
 * 
 * ****************************************************************************/
public class Game {
	private AI ai;
	private Board board;
	private Move move;
	private char playerChar;
	private GameStatus currentStatus = GameStatus.IN_PROGRESS; //Game is in progress as soon as it starts.

	/** Constructor ********************************************************************
	 * 
	 * Instantiates a new Game. 
	 * 
	 * @pre		-	
	 * @params	- playerIsX: True if X, false if O.
	 * 			  challenging: True if player wanted to play against a tougher AI.
	 * @returns - 
	 * @post	- A game is created and class member playerChar is set to the proper
	 * 			  piece; a new board is created and set to the class member; a new
	 * 			  ai is set, according to the player's preference.
	 * ****************************************************************************/
	public Game(boolean playerIsX, boolean challenging){
		if(playerIsX)
			playerChar = 'X';
		else
			playerChar = 'O';

		board = new Board();
		if(challenging)
			ai = new SmartAI(!playerIsX);	//AI is the opposite piece of whatever the player wants.
		else
			ai = new DumbAI(!playerIsX);
	}

	/** placePlayerPiece *****************************************************************
	 * 
	 * Creates a new board with the updated position for the player's piece, and 
	 * checks to see if placing that piece has WON the game. (It's OK, you're free!)
	 * (http://xkcd.com/391)
	 * 
	 * @pre		-	
	 * @params	- i: X Coordinate of the piece
	 * 			  j: Y Codinate of the piece
	 * @returns - TRUE if placement was successfuly
	 * 			  FALSe if not (i.e., (i,j) is not empty)
	 * @post	- Player's piece is placed at i,j, a new board is created to
	 * 			  show the change, and winning conditions are checked.
	 * ****************************************************************************/
	public boolean placePlayerPiece(int i, int j){
		if(board.get(i, j) != ' ')	//Player wants to put the pice in a non-empty spot (CHEATER!)
			return false;
		else{
			move = new Move(i, j, playerChar);	//A new move is created to place on teh board
			board = new Board(board, move);		//A new board is created with the update move

			//Private methods to check for wins
			testRowWin(move.getPiece(), i);
			testColWin(move.getPiece(), j);
			testDiagWin(move.getPiece());
			testAntiDiagWin(move.getPiece());
			testDraw();

			return true;
		}
	}

	/** aiPlacePiece *****************************************************************
	 * 
	 * Creates a new board with the updated position for the ai's piece, and 
	 * checks to see if placing that piece has WON the game. 
	 * 
	 * @pre		-	
	 * @params	- 
	 * @returns - 
	 * @post	- AI's piece is placed and winning conditions are checked.
	 * ****************************************************************************/
	public void aiPlacePiece(){
		move = ai.chooseMove(board);
		board = new Board(board, move);

		testRowWin(move.getPiece(), move.getI());
		testColWin(move.getPiece(), move.getJ());
		testDiagWin(move.getPiece());
		testAntiDiagWin(move.getPiece());
		testDraw();
	}

	/** getBoard *****************************************************************
	 * 
	 * Gets the current board....
	 * @pre		-	
	 * @params	- 
	 * @returns - The current board....
	 * @post	- The board which is current is returned.
	 * ****************************************************************************/
	public Board getBoard(){
		return board;

	}

	/** getStatus *****************************************************************
	 * 
	 * Gets the current status.
	 * @pre		-	
	 * @params	- 
	 * @returns - Current status of the game.
	 * @post	- 
	 * ****************************************************************************/
	public GameStatus getStatus(){
		return currentStatus;
	}

	//** Private methods used to test winning conditions **/

	/** testDraw *****************************************************************
	 * 
	 * Calls the isFull method to see if the board is full, in which case currentStatus
	 * becomes draw.
	 * @pre		-	
	 * @params	- 
	 * @returns - 
	 * @post	- currentStatus is set to DRAW if the gameboard is full.
	 * ****************************************************************************/
	private void testDraw(){
		if(currentStatus == GameStatus.IN_PROGRESS)
			if(board.isFull())
				currentStatus = GameStatus.DRAW;
	}

	/** testRowWin *****************************************************************
	 * 
	 * Iterates through the rows to see if there is 3 of the same game piece in a row.
	 * Called from placePlayerPiece or aiPiece, uses the row index provided in each.
	 * @pre		-	
	 * @params	- c: Character to check for (X or O)
	 * 			  i: The row index of calling function's move
	 * @returns - 
	 * @post	- CurrentStatus is updated as X_WON or O_WON depending on the char,
	 * 			  if said event has occurred.
	 * ****************************************************************************/
	private void testRowWin(char c, int i){
		if(currentStatus == GameStatus.IN_PROGRESS)	//Only need to check if in progress. Prevents updates if win already occurred.
			for (int j = 0; j < 3; j++){	//Iterate through 3 col positions.
				if(board.get(i, j) != c)	//We're checking for a single char 3 times, 
					break;					//so if one col doesn't contain it, why continue?
				if (j == 2)	//3 in a row
					if(c == 'X')
						currentStatus = GameStatus.X_WON;
					else
						currentStatus = GameStatus.O_WON;
			}
	}

	/** testColWin *****************************************************************
	 * 
	 * Iterates through the cols to see if there is 3 of the same game piece in a col.
	 * Called from placePlayerPiece or aiPiece, uses the col index provided in each.
	 * @pre		-	
	 * @params	- c: Character to check for (X or O)
	 * 			  j: The col index of calling function's move
	 * @returns - 
	 * @post	- CurrentStatus is updated as X_WON or O_WON depending on the char,
	 * 			  if said event has occurred.
	 * ****************************************************************************/
	private void testColWin(char c, int j){
		if(currentStatus == GameStatus.IN_PROGRESS)
			for (int i = 0; i < 3; i++){
				if(board.get(i, j) != c)	//Same as testRowWin, except i is changing not j
					break;
				if (i == 2)
					if(c == 'X')
						currentStatus = GameStatus.X_WON;
					else
						currentStatus = GameStatus.O_WON;
			}
	}

	/** testDiagWin *****************************************************************
	 * 
	 * Iterates through the main diagnol (top left to bottom right) to check for 3
	 * of the same piece.
	 * 
	 * @pre		-	
	 * @params	- c: Character to check for (X or O)
	 * @returns - 
	 * @post	- CurrentStatus is updated as X_WON or O_WON depending on the char,
	 * 			  if said event has occurred.
	 * ****************************************************************************/
	private void testDiagWin(char c){
		if(currentStatus == GameStatus.IN_PROGRESS)
			for (int i = 0; i < 3; i++){
				if(board.get(i, i) != c)	//Main diag has coordinates 00, 11, 22
					break;
				if (i == 2)
					if(c == 'X')
						currentStatus = GameStatus.X_WON;
					else
						currentStatus = GameStatus.O_WON;
			}
	}

	/** testAntiDiagWin *****************************************************************
	 * 
	 * Checks the anti-diagnol (top right to bottom left) for 3 of the same char.
	 * @pre		-	
	 * @params	- c: Character to check for (X or O)
	 * @returns - 
	 * @post	- CurrentStatus is updated as X_WON or O_WON depending on the char,
	 * 			  if said event has occurred.
	 * ****************************************************************************/
	private void testAntiDiagWin(char c){
		if(currentStatus == GameStatus.IN_PROGRESS)
			for (int i = 0; i < 3; i++){
				if(board.get(i, 2-i) != c)	//Anti-diag coordinates are 02 01 00;
					break;
				if (i == 2)
					if(c == 'X')
						currentStatus = GameStatus.X_WON;
					else
						currentStatus = GameStatus.O_WON;
			}

	}

}
